﻿//-----------------------------------------------------------------------
// <copyright file="StringHelpers.cs" company="AFS">
// Copyright (c) AFS. All rights reserved.
//
//    This file is part of Chatbox.
//
//    Chatbox is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    any later version.
//
//    Chatbox is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with Chatbox. If not, see http://www.gnu.org/copyleft/lesser.html
// </copyright>
//----------------------------------------------------------------------- 

namespace ChatBox.Utility
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Extension methods for strings
    /// </summary>
    public static class StringHelpers
    {
        /// <summary>
        /// Test if a string has more than just whitespace
        /// </summary>
        /// <param name="value">the string to test</param>
        /// <returns>true if the string is null, empty or contains only white space</returns>
        public static bool IsNullEmptyOrWhiteSpace(this string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return true;
            }

            return string.IsNullOrEmpty(value.Trim());
        }

        /// <summary>
        /// Test if a string has more than just whitespace
        /// </summary>
        /// <param name="value">the string to test</param>
        /// <returns>true if the string is nto just null, empty or contains only white space</returns>
        public static bool HasContent(this string value)
        {
            return !value.IsNullEmptyOrWhiteSpace();
        }

        /// <summary>
        /// True if the string starts with one of the prefixes
        /// </summary>
        /// <param name="text">the text to check</param>
        /// <param name="prefixes">the prefixes to check it against</param>
        /// <param name="comparisonType">the string comparison mode</param>
        /// <returns>true if the text matches one of the prefixes</returns>
        public static bool StartsWithOneOf(this string text, IEnumerable<string> prefixes, StringComparison comparisonType)
        {
            foreach (string test in prefixes)
            {
                if (text.StartsWith(test, comparisonType))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Remove the prefix from the string if it is present
        /// </summary>
        /// <param name="text">the text to work with</param>
        /// <param name="prefix">the prefix to remove</param>
        /// <returns>the altered string</returns>
        public static string TrimPrefix(this string text, string prefix)
        {
            if (text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
            {
                return text.Substring(prefix.Length);
            }
                
            return text;
        }

        /// <summary>
        /// Remove the suffix from the string if it is present
        /// </summary>
        /// <param name="text">the text to work with</param>
        /// <param name="suffix">the suffix to remove</param>
        /// <returns>the altered string</returns>
        public static string TrimSuffix(this string text, string suffix)
        {
            if (text.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
            {
                return text.Substring(0, text.Length - suffix.Length);
            }
            else
            {
                return text;
            }
        }

        /// <summary>
        /// Trim prefix and suffix
        /// </summary>
        /// <param name="text">the text to work with</param>
        /// <param name="prefix">the prefix to remove</param>
        /// <param name="suffix">the suffix to remove</param>
        /// <returns>the altered string</returns>
        public static string TrimPrefixSuffix(this string text, string prefix, string suffix)
        {
            return text.TrimPrefix(prefix).TrimSuffix(suffix);
        }

        /// <summary>
        /// Trim prefix and suffix
        /// </summary>
        /// <param name="text">the text to work with</param>
        /// <param name="prefixSuffix">the prefix and suffix</param>
        /// <returns>the altered string</returns>
        public static string TrimPrefixSuffix(this string text, string prefixSuffix)
        {
            return text.TrimPrefixSuffix(prefixSuffix, prefixSuffix);
        }

        /// <summary>
        /// test if the text both starts and ends with the test string
        /// </summary>
        /// <param name="text">the text to test</param>
        /// <param name="prefixSuffix">the prefix/suffix to look for</param>
        /// <returns>rue if the text both starts and ends with the test string</returns>
        public static bool StartsAndEndsWith(this string text, string prefixSuffix)
        {
            return text.StartsWith(prefixSuffix, StringComparison.OrdinalIgnoreCase)
                && text.EndsWith(prefixSuffix, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// return text before the specified match
        /// </summary>
        /// <param name="text">the text to process</param>
        /// <param name="match">the substring to match</param>
        /// <returns>the returned prefix</returns>
        public static string Before(this string text, string match)
        {
            int index = text.IndexOf(match, StringComparison.OrdinalIgnoreCase);
            if (index == -1)
            {
                return text;
            }
            else
            {
                return text.Substring(0, index);
            }
        }

        /// <summary>
        /// return text after the specified match
        /// </summary>
        /// <param name="text">the text to process</param>
        /// <param name="match">the substring to match</param>
        /// <returns>the returned text after the match</returns>
        public static string After(this string text, string match)
        {
            int index = text.IndexOf(match, StringComparison.OrdinalIgnoreCase);
            if (index == -1)
            {
                return text;
            }
            else
            {
                return text.Substring(index + match.Length);
            }
        }

        /// <summary>
        /// return text from the specified match onwards
        /// </summary>
        /// <param name="text">the text to process</param>
        /// <param name="match">the substring to match</param>
        /// <returns>the returned text from the match onwards</returns>
        public static string MatchOnwards(this string text, string match)
        {
            int index = text.IndexOf(match, StringComparison.OrdinalIgnoreCase);
            if (index == -1)
            {
                return text;
            }
            else
            {
                return text.Substring(index);
            }
        }

        /// <summary>
        /// return text before and including the specified match
        /// </summary>
        /// <param name="text">the text to process</param>
        /// <param name="match">the substring to match</param>
        /// <returns>the returned prefix</returns>
        public static string UpToMatch(this string text, string match)
        {
            int index = text.IndexOf(match, StringComparison.OrdinalIgnoreCase);
            if (index == -1)
            {
                return text;
            }
            else
            {
                return text.Substring(0, index + match.Length);
            }
        }

        /// <summary>
        /// Split text into html tag params
        /// </summary>
        /// <param name="inputText">the ext contain the params e.g. "a=b c=d"</param>
        /// <returns>the dictionary of key value pairs</returns>
        public static IDictionary<string, string> SplitParameters(string inputText)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            int equalsIndex = inputText.IndexOf("=", StringComparison.OrdinalIgnoreCase);

            while (equalsIndex > 0)
            {
                string key = inputText.Substring(0, equalsIndex).Trim();
                string value = inputText.Substring(equalsIndex + 1).Trim();

                int valueEnd;
                if (value.StartsWith("\"", StringComparison.OrdinalIgnoreCase))
                {
                    valueEnd = value.IndexOf("\"", 1, StringComparison.OrdinalIgnoreCase);
                }
                else
                {
                    valueEnd = value.IndexOf(" ", StringComparison.OrdinalIgnoreCase);
                }

                string rest;
                if (valueEnd > 0)
                {
                    rest = value.Substring(valueEnd + 1);
                    value = value.Substring(0, valueEnd + 1).TrimEnd();
                }
                else
                {
                    rest = string.Empty;
                }

                if (value.StartsAndEndsWith("\""))
                {
                    value = value.TrimPrefixSuffix("\"");
                }

                result.Add(key, value);

                inputText = rest;
                equalsIndex = inputText.IndexOf("=", StringComparison.OrdinalIgnoreCase);
            }

            return result;
        }

        /// <summary>
        /// Split a string at a token
        /// </summary>
        /// <param name="text">The text to split</param>
        /// <param name="startToken">the token that starts the split</param>
        /// <param name="endToken">the token that ends the split</param>
        /// <returns>the split text</returns>
        public static IList<string> SplitAtTokens(this string text, string startToken, string endToken)
        {
            List<string> result = new List<string>();

            SplitAtTokensRecursive(result, text, startToken, endToken, true);

            return result;
        }

        /// <summary>
        /// Recursive worker for SplitAtTokens
        /// </summary>
        /// <param name="accumulator">List to accumulate the texts</param>
        /// <param name="text">the text to split</param>
        /// <param name="startToken">the token that starts the split</param>
        /// <param name="endToken">the token that ends the split</param>
        /// <param name="multipleTokens">If true, look for multiple tokens</param>
        private static void SplitAtTokensRecursive(ICollection<string> accumulator, string text, string startToken, string endToken, bool multipleTokens)
        {
            if (String.IsNullOrEmpty(text))
            {
                return;
            }

            int startIndex = text.IndexOf(startToken, StringComparison.OrdinalIgnoreCase);
            int endIndex = -1;

            if (startIndex >= 0)
            {
                endIndex = text.IndexOf(endToken, startIndex + startToken.Length, StringComparison.OrdinalIgnoreCase);
            }

            if ((startIndex < 0) || (endIndex < 0))
            {
                // split not found, return unchanged
                accumulator.Add(text);
            }
            else
            {
                // it was found, return the rest
                int afterEnd = endIndex + endToken.Length;
                string start = text.Substring(0, startIndex);
                string mid = text.Substring(startIndex, afterEnd - startIndex);
                string rest = text.Substring(afterEnd);

                if (startIndex > 0)
                {
                    accumulator.Add(start);
                }

                accumulator.Add(mid);

                if (multipleTokens)
                {
                    // split the rest
                    SplitAtTokensRecursive(accumulator, rest, startToken, endToken, true);
                }
                else
                {
                    // one is enough
                    accumulator.Add(rest);
                }
            }
        }
    }
}
